/*-------------------------------------------------------------------------
 *
 * pg_type.h
 *	  “类型”系统目录的定义 (pg_type)
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/catalog/pg_type.h
 *
 * NOTES
 *	  Catalog.pm 模块读取此文件并推导模式
 *	  信息。
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_TYPE_H
#define PG_TYPE_H

#include "catalog/genbki.h"
#include "catalog/objectaddress.h"
#include "catalog/pg_type_d.h"
#include "nodes/nodes.h"

/* ----------------
 *		pg_type 定义。 cpp 将其转换为
 *		typedef struct FormData_pg_type
 *
 *		pg_type 实例中的某些值被复制到
 *		pg_attribute 实例中。某些部分的 Postgres 使用 pg_type 复制，
 *		而其他部分使用 pg_attribute 复制，因此它们必须匹配。
 *		有关详细信息，请参见结构 FormData_pg_attribute。
 * ----------------
 */
CATALOG(pg_type,1247,TypeRelationId) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71,TypeRelation_Rowtype_Id) BKI_SCHEMA_MACRO
{
	Oid			oid;			/* oid */

	/* 类型名称 */
	NameData	typname;

	/* 包含此类型的名称空间的 OID */
	Oid			typnamespace BKI_DEFAULT(pg_catalog) BKI_LOOKUP(pg_namespace);

	/* 类型所有者 */
	Oid			typowner BKI_DEFAULT(POSTGRES) BKI_LOOKUP(pg_authid);

	/*
	 * 对于固定大小的类型，typlen 是用于表示此类型值的字节数，
	 * 例如，int4 的值为 4。但对于可变长度的类型，typlen 为负数。
	 * 我们使用 -1 来表示“varlena”类型（即具有长度字的类型），
	 * 使用 -2 来表示以 null 结尾的 C 字符串。
	 */
	int16		typlen BKI_ARRAY_DEFAULT(-1);

	/*
	 * typbyval 决定内部 Postgres 例程是按值还是按引用传递此类型的值。
	 * 如果长度不是 1、2 或 4（在 8 字节 Datum 机器上是 8），typbyval 最好为假。
	 * 可变长度类型始终按引用传递。请注意，即使长度允许按值传递，typbyval
	 * 也可以为假；例如，类型 macaddr8 在 Datum 为 8 字节时是按引用传递的。
	 */
	bool		typbyval BKI_ARRAY_DEFAULT(f);

	/*
	 * typtype 为基础类型时为 'b'，为复合类型（例如，表的行类型）时为 'c'，
	 * 为域时为 'd'，为枚举类型时为 'e'，为伪类型时为 'p'，或为范围类型时为 'r'。
	 * （使用下面的 TYPTYPE 宏。）
	 *
	 * 如果 typtype 为 'c'，则 typrelid 是类在 pg_class 中的条目的 OID。
	 */
	char		typtype BKI_DEFAULT(b) BKI_ARRAY_DEFAULT(b);

	/*
	 * typcategory 和 typispreferred 帮助解析器区分首选
	 * 和非首选的强制转换。类别可以是任何单个 ASCII
	 * 字符（但不能是 \0）。用于内置类型的类别由下面
	 * 的 TYPCATEGORY 宏标识。
	 */

	/* 任意类型分类 */
	char		typcategory BKI_ARRAY_DEFAULT(A);

	/* 该类型在其类别中是否“首选”？ */
	bool		typispreferred BKI_DEFAULT(f) BKI_ARRAY_DEFAULT(f);

	/*
	 * 如果 typisdefined 为假，则该条目仅是一个占位符
	 * （前向引用）。我们知道该类型的名称和所有者，但
	 * 还不知道其他任何信息。
	 */
	bool		typisdefined BKI_DEFAULT(t);

	/* 该类型数组的分隔符 */
	char		typdelim BKI_DEFAULT(',');

	/* 如果是复合类型，则关联的 pg_class OID，否则为 0 */
	Oid			typrelid BKI_DEFAULT(0) BKI_ARRAY_DEFAULT(0) BKI_LOOKUP_OPT(pg_class);

	/*
	 * 特定于类型的下标处理程序。如果 typsubscript 为 0，
	 * 则意味着该类型不支持下标。请注意，系统的各个部分
	 * 仅当其 typsubscript 为 array_subscript_handler 时，
	 * 才将类型视为“真正的”数组类型。
	 */
	regproc		typsubscript BKI_DEFAULT(-) BKI_ARRAY_DEFAULT(array_subscript_handler) BKI_LOOKUP_OPT(pg_proc);

	/*
	 * 如果 typelem 不为 0，则它标识 pg_type 中的另一个行，
	 * 定义通过下标操作生成的类型。如果 typsubscript 为
	 * 0，则该值应为 0。然而，如果处理程序不需要 typelem
	 * 来确定下标结果类型，则当 typsubscript 不为 0 时，
	 * 它可以为 0。请注意，typelem 依赖关系被视为暗示
	 * 该类型中元素类型的物理包含；因此，元素类型上的 DDL
	 * 更改可能会受到该类型存在的限制。
	 */
	Oid			typelem BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_type);

	/*
	 * 如果存在一个“真正的”数组类型，其元素类型为该类型，
	 * typarray 链接到它。如果没有关联的“真正的”数组类型，则为 0。
	 */
	Oid			typarray BKI_DEFAULT(0) BKI_ARRAY_DEFAULT(0) BKI_LOOKUP_OPT(pg_type);

	/*
	 * I/O 转换过程用于数据类型。
	 */

	/* 文本格式（必需） */
	regproc		typinput BKI_ARRAY_DEFAULT(array_in) BKI_LOOKUP(pg_proc);
	regproc		typoutput BKI_ARRAY_DEFAULT(array_out) BKI_LOOKUP(pg_proc);

	/* 二进制格式（可选） */
	regproc		typreceive BKI_ARRAY_DEFAULT(array_recv) BKI_LOOKUP_OPT(pg_proc);
	regproc		typsend BKI_ARRAY_DEFAULT(array_send) BKI_LOOKUP_OPT(pg_proc);

	/*
	 * I/O 函数用于可选类型修饰符。
	 */
	regproc		typmodin BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);
	regproc		typmodout BKI_DEFAULT(-) BKI_LOOKUP_OPT(pg_proc);

	/*
	 * 自定义 ANALYZE 过程用于数据类型（0 选择默认）。
	 */
	regproc		typanalyze BKI_DEFAULT(-) BKI_ARRAY_DEFAULT(array_typanalyze) BKI_LOOKUP_OPT(pg_proc);

	/* ----------------
	 * typalign 是存储此类型值时所需的对齐方式。
	 * 它适用于磁盘存储以及 Postgres 内部值的大多数表示形式。
	 * 当多个值连续存储时，例如在磁盘上表示完整行时，
	 * 会在此类型数据项之前插入填充，以便其在指定边界上开始。
	 * 对齐参考是序列中第一个数据项的开头。
	 *
	 * 'c' = CHAR 对齐，即无需对齐。
	 * 's' = SHORT 对齐（在大多数机器上为 2 字节）。
	 * 'i' = INT 对齐（在大多数机器上为 4 字节）。
	 * 'd' = DOUBLE 对齐（在许多机器上为 8 字节，但并非全部）。
	 * （使用下面的 TYPALIGN 宏来处理这些。）
	 *
	 * 请参见 include/access/tupmacs.h 中计算这些对齐要求的宏。
	 * 另外请注意，存储“压缩”可变长度类型时，我们允许违反名义对齐；
	 * TOAST 机制负责从大多数代码中隐藏这一点。
	 *
	 * 注意：对于在系统表中使用的类型，pg_type 中定义的大小和对齐必须与
	 * 编译器在表示表行的结构中布局字段的方式一致。
	 * ----------------
	 */
	char		typalign;

	/* ----------------
	 * typstorage 指示该类型是否为 TOAST 准备，以及此类型属性的默认策略应为何。
	 *
	 * 'p' PLAIN	  类型未为 TOAST 准备
	 * 'e' EXTERNAL   可能有外部存储，不要尝试压缩
	 * 'x' EXTENDED   尝试压缩并在需要时存储为外部
	 * 'm' MAIN		  类似于 'x'，但尽量保持在主元组中
	 * （使用下面的 TYPSTORAGE 宏处理这些。）
	 *
	 * 请注意，'m' 字段也可以移动到二级存储，但只能作为最后的手段
	 * （'e' 和 'x' 字段首先被移动）。
	 * ----------------
	 */
	char		typstorage BKI_DEFAULT(p) BKI_ARRAY_DEFAULT(x);

	/*
	 * 此标志表示对该数据类型的“NOT NULL”约束。
	 *
	 * 如果为真，使用此数据类型的相应表列的 attnotnull 列将始终强制
	 * NOT NULL 约束。
	 *
	 * 主要用于域类型。
	 */
	bool		typnotnull BKI_DEFAULT(f);

	/*
	 * 域使用 typbasetype 显示基础（或域）类型，域是基于此的。
	 * 如果类型不是域，则为零。
	 */
	Oid			typbasetype BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_type);

	/*
	 * 域使用 typtypmod 记录将应用于其基础类型的 typmod
	 * （如果基础类型不使用 typmod，则为 -1）。
	 * 如果此类型不是域，则为 -1。
	 */
	int32		typtypmod BKI_DEFAULT(-1);

	/*
	 * typndims 是声明的数组域类型的维度数量
	 * （即 typbasetype 是一个数组类型）。否则为零。
	 */
	int32		typndims BKI_DEFAULT(0);

	/*
	 * 排序规则：如果类型无法使用排序，值为 0，
	 * 非零（通常为 DEFAULT_COLLATION_OID）表示可排序基础类型，
	 * 可能对可排序类型的域使用其他 OID。
	 */
	Oid			typcollation BKI_DEFAULT(0) BKI_LOOKUP_OPT(pg_collation);

#ifdef CATALOG_VARLEN			/* 可变长度字段从这里开始 */

	/*
	 * 如果 typdefaultbin 不为 NULL，它是类型的默认表达式的 nodeToString 表示。
	 * 当前这只用于域。
	 */
	pg_node_tree typdefaultbin BKI_DEFAULT(_null_) BKI_ARRAY_DEFAULT(_null_);

	/*
	 * 如果类型没有关联的默认值，则 typdefault 为 NULL。
	 * 如果 typdefaultbin 不为 NULL，则 typdefault 必须包含由 typdefaultbin 表示的默认表达式的人类可读版本。
	 * 如果 typdefaultbin 为 NULL 且 typdefault 不为 NULL，
	 * 则 typdefault 是该类型默认值的外部表示，
	 * 可以传递给类型的输入转换器以生成常量。
	 */
	text		typdefault BKI_DEFAULT(_null_) BKI_ARRAY_DEFAULT(_null_);

	/*
	 * 访问权限
	 */
	aclitem		typacl[1] BKI_DEFAULT(_null_);
#endif
} FormData_pg_type;

/* ----------------
 *		Form_pg_type 对应指向具有 pg_type 关系格式的行的指针。
 * ----------------
 */
typedef FormData_pg_type *Form_pg_type;

DECLARE_TOAST(pg_type, 4171, 4172);

DECLARE_UNIQUE_INDEX_PKEY(pg_type_oid_index, 2703, TypeOidIndexId, on pg_type using btree(oid oid_ops));
DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, TypeNameNspIndexId, on pg_type using btree(typname name_ops, typnamespace oid_ops));

#ifdef EXPOSE_TO_CLIENT_CODE

/*
 * poor-mans-enumerated-type 列的宏
 */
#define  TYPTYPE_BASE		'b' /* 基本类型（普通标量类型） */
#define  TYPTYPE_COMPOSITE	'c' /* 复合类型（例如，表的行类型） */
#define  TYPTYPE_DOMAIN		'd' /* 另一种类型的域 */
#define  TYPTYPE_ENUM		'e' /* 枚举类型 */
#define  TYPTYPE_MULTIRANGE	'm' /* 多范围类型 */
#define  TYPTYPE_PSEUDO		'p' /* 伪类型 */
#define  TYPTYPE_RANGE		'r' /* 范围类型 */

#define  TYPCATEGORY_INVALID	'\0'	/* 不是允许的类别 */
#define  TYPCATEGORY_ARRAY		'A'
#define  TYPCATEGORY_BOOLEAN	'B'
#define  TYPCATEGORY_COMPOSITE	'C'
#define  TYPCATEGORY_DATETIME	'D'
#define  TYPCATEGORY_ENUM		'E'
#define  TYPCATEGORY_GEOMETRIC	'G'
#define  TYPCATEGORY_NETWORK	'I' /* 思考 INET */
#define  TYPCATEGORY_NUMERIC	'N'
#define  TYPCATEGORY_PSEUDOTYPE 'P'
#define  TYPCATEGORY_RANGE		'R'
#define  TYPCATEGORY_STRING		'S'
#define  TYPCATEGORY_TIMESPAN	'T'
#define  TYPCATEGORY_USER		'U'
#define  TYPCATEGORY_BITSTRING	'V' /* 嗯 ... "varbit"? */
#define  TYPCATEGORY_UNKNOWN	'X'
#define  TYPCATEGORY_INTERNAL	'Z'

#define  TYPALIGN_CHAR			'c' /* 字符对齐（即未对齐） */
#define  TYPALIGN_SHORT			's' /* 短整型对齐（通常为 2 字节） */
#define  TYPALIGN_INT			'i' /* 整型对齐（通常为 4 字节） */
#define  TYPALIGN_DOUBLE		'd' /* 双精度对齐（通常为 8 字节） */

#define  TYPSTORAGE_PLAIN		'p' /* 类型未准备好进行压缩 */
#define  TYPSTORAGE_EXTERNAL	'e' /* 可压缩，不要尝试压缩 */
#define  TYPSTORAGE_EXTENDED	'x' /* 完全可压缩 */
#define  TYPSTORAGE_MAIN		'm' /* 像 'x' 但尝试内联存储 */

/* 类型 OID 是否为多态伪类型？（注意多次评估） */
#define IsPolymorphicType(typid)  \
	(IsPolymorphicTypeFamily1(typid) || \
	 IsPolymorphicTypeFamily2(typid))

/* 不应使用这些宏的代码不应属于多态类型解析： */
#define IsPolymorphicTypeFamily1(typid)  \
	((typid) == ANYELEMENTOID || \
	 (typid) == ANYARRAYOID || \
	 (typid) == ANYNONARRAYOID || \
	 (typid) == ANYENUMOID || \
	 (typid) == ANYRANGEOID || \
	 (typid) == ANYMULTIRANGEOID)

#define IsPolymorphicTypeFamily2(typid)  \
	((typid) == ANYCOMPATIBLEOID || \
	 (typid) == ANYCOMPATIBLEARRAYOID || \
	 (typid) == ANYCOMPATIBLENONARRAYOID || \
	 (typid) == ANYCOMPATIBLERANGEOID || \
	 (typid) == ANYCOMPATIBLEMULTIRANGEOID)

/* 这是否是一个“真实的”数组类型？（需要 fmgroids.h） */
#define IsTrueArrayType(typeForm)  \
	(OidIsValid((typeForm)->typelem) && \
	 (typeForm)->typsubscript == F_ARRAY_SUBSCRIPT_HANDLER)

/*
 * 对古老拼写的 pg_type OID 宏的向后兼容性。
 * 不要在新代码中使用这些名称。
 */
#define CASHOID	MONEYOID
#define LSNOID	PG_LSNOID

#endif							/* EXPOSE_TO_CLIENT_CODE */


extern ObjectAddress TypeShellMake(const char *typeName,
								   Oid typeNamespace,
								   Oid ownerId);

extern ObjectAddress TypeCreate(Oid newTypeOid,
								const char *typeName,
								Oid typeNamespace,
								Oid relationOid,
								char relationKind,
								Oid ownerId,
								int16 internalSize,
								char typeType,
								char typeCategory,
								bool typePreferred,
								char typDelim,
								Oid inputProcedure,
								Oid outputProcedure,
								Oid receiveProcedure,
								Oid sendProcedure,
								Oid typmodinProcedure,
								Oid typmodoutProcedure,
								Oid analyzeProcedure,
								Oid subscriptProcedure,
								Oid elementType,
								bool isImplicitArray,
								Oid arrayType,
								Oid baseType,
								const char *defaultTypeValue,
								char *defaultTypeBin,
								bool passedByValue,
								char alignment,
								char storage,
								int32 typeMod,
								int32 typNDims,
								bool typeNotNull,
								Oid typeCollation);

extern void GenerateTypeDependencies(HeapTuple typeTuple,
									 Relation typeCatalog,
									 Node *defaultExpr,
									 void *typacl,
									 char relationKind, /* 仅适用于关系
														 * 行类型 */
									 bool isImplicitArray,
									 bool isDependentType,
									 bool makeExtensionDep,
									 bool rebuild);

extern void RenameTypeInternal(Oid typeOid, const char *newTypeName,
							   Oid typeNamespace);

extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);

extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
							  Oid typeNamespace);

extern char *makeMultirangeTypeName(const char *rangeTypeName,
									Oid typeNamespace);

#endif							/* PG_TYPE_H */
